home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / quicktime / quicktimeintro / wiredsprites / start code / qtwiredsprites.c next >
Encoding:
Text File  |  2000-10-06  |  16.1 KB  |  418 lines

  1. //////////
  2. //
  3. //    File:        QTWiredSprites.c
  4. //
  5. //    Contains:    QuickTime wired sprites support for QuickTime movies.
  6. //
  7. //    Written by:    Sean Allen
  8. //    Revised by:    Chris Flick and Tim Monroe
  9. //                Based (heavily!) on the existing MakeActionSpriteMovie.c code written by Sean Allen.
  10. //
  11. //    Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  12. //
  13. //    Change History (most recent first):
  14. //
  15. //       <5>         03/20/00    rtm        made changes to get things running under CarbonLib
  16. //       <4>         07/30/99    rtm        added QTWired_AddCursorChangeOnMouseOver to illustrate new
  17. //                                    sprite-as-button behaviors added in QT4; added this action to penguin one
  18. //       <3>         09/30/98    rtm        tweaked call to AddMovieResource to create single-fork movies
  19. //       <2>         03/26/98    rtm        made fixes for Windows compiles
  20. //       <1>         03/25/98    rtm        first file; integrated existing code with shell framework
  21. //       
  22. //
  23. //    This sample code creates a wired sprite movie containing one sprite track. The sprite track contains
  24. //    six sprites: two penguins and four buttons.
  25. //    
  26. //    The four buttons are initially invisible. When the mouse enters (or "rolls over") a button, it appears.
  27. //    When the mouse is clicked inside a button, its image changes to its "pressed" image. When the mouse
  28. //    is released, its image changes back to its "unpressed" image. If the mouse is released inside the button, 
  29. //    an action is triggered. The buttons perform the actions of go to beginning of movie, step backward,
  30. //    step forward, and go to end of movie.
  31. //    
  32. //    The first penguin shows all of the buttons when the mouse enters it, and hides them when the mouse exits.
  33. //    The first penguin is the only sprite that has properties that are overriden by the override sprite samples.
  34. //    These samples override its matrix (in order to move it) and its image index (in order to make it "waddle").
  35. //    
  36. //    When the mouse is clicked on the second penguin, it changes its image index to it's "eyes closed" image.
  37. //    When the mouse is released, it changes back to its normal image. This makes it appear to blink when clicked on.
  38. //    When the mouse is released over the penguin, several actions are triggered. Both penguins' graphics states are 
  39. //    toggled between copyMode and blendMode, and the movie's rate is toggled between zero and one.
  40. //    
  41. //    The second penguin moves once per second. This occurs whether the movie's rate is currently zero or one,
  42. //    because it is being triggered by a gated idle event. When the penguin receives the idle event, it changes
  43. //    its matrix using an action which uses min, max, delta, and wraparound options.
  44. //
  45. //    The movie's looping mode is set to palindrome by a frame-loaded action.
  46. //
  47. //    So, our general strategy is as follows (though perhaps not in the order listed):
  48. //
  49. //        (1) Create a new movie file with a single sprite track.
  50. //        (2) Assign the "no controller" movie controller to the movie.
  51. //        (3) Set the sprite track's background color, idle event frequency, and hasActions properties.
  52. //        (4) Convert our PICT resources to animation codec images with transparency.
  53. //        (5) Create a key frame sample containing six sprites and all of their shared images.
  54. //        (6) Assign the sprites their initial property values.
  55. //        (7)    Create a frameLoaded event for the key frame.
  56. //        (8)    Create some override samples that override the matrix and image index properties of
  57. //            the first penguin sprite.
  58. //
  59. //    NOTES:
  60. //        
  61. //    *** (1) ***
  62. //    There are event types other that mouse related events (for instance, Idle and FrameLoaded).
  63. //    Idle events are independent of the movie's rate, and they can be gated so they are send at most
  64. //    every n ticks. In our sample movie, the second penguin moves when the movie's rate is zero,
  65. //    and moves only once per second because of the value of the sprite track's idleEventFrequencey property.
  66. //        
  67. //    *** (2) ***
  68. //    Multiple actions may be executed in response to a single event. In our sample movie, rolling over
  69. //    the first penguin shows and hides four different buttons.
  70. //        
  71. //    *** (3) ***
  72. //    Actions may target any sprite or track in the movie. In our sample movie, clicking on one penguin
  73. //    changes the graphics mode of the other.
  74. //        
  75. //    *** (4) ***
  76. //    Conditional and looping control structures are supported. In our sample movie, the second penguin
  77. //    uses the "case statement" action.
  78. //        
  79. //    *** (5) ***
  80. //    Sprite track variables that have not been set have a default value of zero. (The second penguin's
  81. //    conditional code relies on this.)
  82. //        
  83. //    *** (6) ***
  84. //    Wired sprites were previously known as "action sprites". Don't let the names of some of the utility
  85. //    functions confuse you. We'll try to update the source code as time permits.
  86. //        
  87. //    *** (7) ***
  88. //    Penguins don't fly, but I hear they totally shred halfpipes on snowboards.
  89. //
  90. //////////
  91.  
  92.  
  93. // header files
  94. #include "QTWiredSprites.h"
  95.  
  96.  
  97. //////////
  98. //
  99. // QTWired_CreateWiredSpritesMovie
  100. // Create a QuickTime movie containing a wired sprites track.
  101. //
  102. //////////
  103.  
  104. OSErr QTWired_CreateWiredSpritesMovie (void)
  105. {
  106.     short                    myResRefNum = 0;
  107.     short                    myResID = movieInDataForkResID;
  108.     Movie                    myMovie = NULL;
  109.     Track                    myTrack;
  110.     Media                    myMedia;
  111.     FSSpec                    myFile;
  112.     Boolean                    myIsSelected = false;
  113.     Boolean                    myIsReplacing = false;    
  114.     StringPtr                 myPrompt = QTUtils_ConvertCToPascalString(kWiredSavePrompt);
  115.     StringPtr                 myFileName = QTUtils_ConvertCToPascalString(kWiredSaveFileName);
  116.     QTAtomContainer            mySample = NULL;
  117.     QTAtomContainer            myActions = NULL;
  118.     QTAtomContainer            myBeginButton, myPrevButton, myNextButton, myEndButton;
  119.     QTAtomContainer            myPenguinOne, myPenguinTwo, myPenguinOneOverride;
  120.     QTAtomContainer            myBeginActionButton, myPrevActionButton, myNextActionButton, myEndActionButton;
  121.     QTAtomContainer            myPenguinOneAction, myPenguinTwoAction;
  122.     RGBColor                myKeyColor;
  123.     Point                    myLocation;
  124.     short                    isVisible, myLayer, myIndex, myID, i, myDelta;
  125.     Boolean                    hasActions;
  126.     long                    myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
  127.     OSType                    myType = FOUR_CHAR_CODE('none');
  128.     UInt32                    myFrequency;
  129.     QTAtom                    myEventAtom;
  130.     long                    myLoopingFlags;
  131.     ModifierTrackGraphicsModeRecord        myGraphicsMode;
  132.     OSErr                    myErr = noErr;
  133.  
  134.     //////////
  135.     //
  136.     // create a new movie file and set its controller type
  137.     //
  138.     //////////
  139.  
  140.     // ask the user for the name of the new movie file
  141.     QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing);
  142.     if (!myIsSelected)
  143.         goto bail;
  144.  
  145. // Step 1.
  146. // Insert "CreateMovieFile.clp" here
  147.  
  148.  
  149. // Step 2.
  150. // Insert "CreateKeyFrameSample.clp" here
  151.  
  152.  
  153. // Step 3.
  154. // Insert "AssignImageGroupIDs.clp" here
  155.  
  156.  
  157. // Step 4.
  158. // Insert "SetSpriteProperties.clp" here
  159.     
  160.  
  161. // Step 5.
  162. // Insert "AddSpriteActions.clp" here
  163.  
  164.  
  165. // Step 6.
  166. // Insert "AddSpriteSampleToMedia.clp" here
  167.  
  168.  
  169. // Step 7.
  170. // Insert "AddOverrideSamples.clp" here
  171.  
  172.  
  173. // Step 8.
  174. // Insert "InsertMediaIntoTrack.clp" here
  175.  
  176.  
  177. // Step 9.
  178. // Insert "SpriteTrackProperties.clp" here
  179.     
  180.  
  181. // Step 10.
  182. // Insert "AddMovieResource.clp" here
  183.  
  184.     
  185. bail:
  186.     free(myPrompt);
  187.     free(myFileName);
  188.  
  189.     if (mySample != NULL)
  190.         QTDisposeAtomContainer(mySample);
  191.  
  192.     if (myBeginButton != NULL)
  193.         QTDisposeAtomContainer(myBeginButton);    
  194.             
  195.     if (myPrevButton != NULL)
  196.         QTDisposeAtomContainer(myPrevButton);
  197.                 
  198.     if (myNextButton != NULL)
  199.         QTDisposeAtomContainer(myNextButton);
  200.                 
  201.     if (myEndButton != NULL)
  202.         QTDisposeAtomContainer(myEndButton);        
  203.         
  204.     if (myResRefNum != 0)
  205.         CloseMovieFile(myResRefNum);
  206.  
  207.     if (myMovie != NULL)
  208.         DisposeMovie(myMovie);
  209.         
  210.     return(myErr);
  211. }
  212.  
  213.  
  214. //////////
  215. //
  216. // QTWired_AddPenguinTwoConditionalActions
  217. // Add actions to the second penguin that transform him (her?) into a two state button
  218. // that plays or pauses the movie.
  219. //
  220. // We are relying on the fact that a "GetVariable" for a variable ID which has never been set
  221. // will return zero. If we needed a different default value, we could initialize it using the
  222. // frameLoaded event.
  223. //
  224. // A higher-level description of the logic is:
  225. // 
  226. //     On MouseUpInside
  227. //        If (GetVariable(DefaultTrack, 1) = 0)
  228. //           SetMovieRate(1)
  229. //           SetSpriteGraphicsMode(DefaultSprite, { blend, grey } )
  230. //           SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5), { ditherCopy, white } )
  231. //           SetVariable(DefaultTrack, 1, 1)
  232. //        ElseIf (GetVariable(DefaultTrack, 1) = 1)
  233. //           SetMovieRate(0)
  234. //           SetSpriteGraphicsMode(DefaultSprite, { ditherCopy, white })
  235. //           SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5), { blend, grey })
  236. //           SetVariable(DefaultTrack, 1, 0)
  237. //        Endif
  238. //     End
  239. // 
  240. //////////
  241.  
  242. OSErr QTWired_AddPenguinTwoConditionalActions (QTAtomContainer theContainer, QTAtom theEventAtom)
  243. {
  244.     QTAtom                                myNewActionAtom, myNewParamAtom, myConditionalAtom;
  245.     QTAtom                                myExpressionAtom, myOperatorAtom, myActionListAtom;
  246.     short                                myParamIndex, myConditionIndex, myOperandIndex;
  247.     float                                myConstantValue;
  248.     QTAtomID                            myVariableID;
  249.     ModifierTrackGraphicsModeRecord        myBlendMode, myCopyMode;
  250.     
  251.     myBlendMode.graphicsMode = blend;
  252.     myBlendMode.opColor.red = myBlendMode.opColor.green = myBlendMode.opColor.blue = 0x8fff;    // grey
  253.  
  254.     myCopyMode.graphicsMode = ditherCopy;
  255.     myCopyMode.opColor.red = myCopyMode.opColor.green = myCopyMode.opColor.blue = 0xffff;        // white
  256.  
  257.     AddActionAtom(theContainer, theEventAtom, kActionCase, &myNewActionAtom);
  258.     
  259.     myParamIndex = 1;
  260.     AddActionParameterAtom(theContainer, myNewActionAtom, myParamIndex, 0, NULL, &myNewParamAtom);
  261.  
  262.     // first condition
  263.     myConditionIndex = 1;
  264.     AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex, &myConditionalAtom);
  265.     AddExpressionContainerAtomType(theContainer, myConditionalAtom, &myExpressionAtom);
  266.     AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo, &myOperatorAtom);
  267.  
  268.     myOperandIndex = 1;
  269.     myConstantValue = kButtonStateOne;
  270.     AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant, myOperandIndex, NULL, myConstantValue);
  271.  
  272.     myOperandIndex = 2;
  273.     myVariableID = kPenguinStateVariableID;
  274.     AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex, 0, NULL, 0, myVariableID);
  275.  
  276.     AddActionListAtom(theContainer, myConditionalAtom, &myActionListAtom);
  277.     AddMovieSetRateAction(theContainer, myActionListAtom, 0, Long2Fix(1));
  278.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, 0, NULL, &myBlendMode, NULL);
  279.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID, &myCopyMode, NULL);
  280.     AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0, kPenguinStateVariableID, kButtonStateTwo, 0, NULL, 0);
  281.                                        
  282.     // second condition
  283.     myConditionIndex = 2;
  284.     AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex, &myConditionalAtom);
  285.     AddExpressionContainerAtomType(theContainer, myConditionalAtom, &myExpressionAtom);
  286.     AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo, &myOperatorAtom);
  287.  
  288.     myOperandIndex = 1;
  289.     myConstantValue = kButtonStateTwo;
  290.     AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant, myOperandIndex, NULL, myConstantValue);
  291.  
  292.     myOperandIndex = 2;
  293.     myVariableID = kPenguinStateVariableID;
  294.     AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex, 0, NULL, 0, myVariableID);
  295.  
  296.     AddActionListAtom(theContainer, myConditionalAtom, &myActionListAtom);
  297.     AddMovieSetRateAction(theContainer, myActionListAtom, 0, Long2Fix(0));
  298.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, 0, NULL, &myCopyMode, NULL);
  299.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID, &myBlendMode, NULL);
  300.     AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0, kPenguinStateVariableID, kButtonStateOne, 0, NULL, 0);
  301.  
  302.     return(noErr);
  303. }
  304.  
  305.  
  306. //////////
  307. //
  308. // QTWired_AddWraparoundMatrixOnIdle
  309. // Add beginning, end, and change matrices to the specified atom container.
  310. //
  311. //////////
  312.  
  313. OSErr QTWired_AddWraparoundMatrixOnIdle (QTAtomContainer theContainer)
  314. {
  315.     MatrixRecord     myMinMatrix, myMaxMatrix, myDeltaMatrix;
  316.     long            myFlags = kActionFlagActionIsDelta | kActionFlagParameterWrapsAround;
  317.     QTAtom            myActionAtom;
  318.     OSErr            myErr = noErr;
  319.     
  320.     myMinMatrix.matrix[0][0] = myMinMatrix.matrix[0][1] = myMinMatrix.matrix[0][2] = EndianS32_NtoB(0xffffffff);
  321.     myMinMatrix.matrix[1][0] = myMinMatrix.matrix[1][1] = myMinMatrix.matrix[1][2] = EndianS32_NtoB(0xffffffff);
  322.     myMinMatrix.matrix[2][0] = myMinMatrix.matrix[2][1] = myMinMatrix.matrix[2][2] = EndianS32_NtoB(0xffffffff);
  323.  
  324.     myMaxMatrix.matrix[0][0] = myMaxMatrix.matrix[0][1] = myMaxMatrix.matrix[0][2] = EndianS32_NtoB(0x7fffffff);
  325.     myMaxMatrix.matrix[1][0] = myMaxMatrix.matrix[1][1] = myMaxMatrix.matrix[1][2] = EndianS32_NtoB(0x7fffffff);
  326.     myMaxMatrix.matrix[2][0] = myMaxMatrix.matrix[2][1] = myMaxMatrix.matrix[2][2] = EndianS32_NtoB(0x7fffffff);
  327.     
  328.     myMinMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((1 * kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
  329.     myMaxMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((3 * kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
  330.  
  331.     SetIdentityMatrix(&myDeltaMatrix);
  332.     myDeltaMatrix.matrix[2][1] = Long2Fix(1);
  333.     
  334.     // change location
  335.     myErr = AddSpriteSetMatrixAction(theContainer, kParentAtomIsContainer, kQTEventIdle, 0, NULL, 0, 0, NULL, &myDeltaMatrix, &myActionAtom);
  336.     if (myErr != noErr)
  337.         goto bail;
  338.  
  339.     myErr = AddActionParameterOptions(theContainer, myActionAtom, 1, myFlags, sizeof(myMinMatrix), &myMinMatrix, sizeof(myMaxMatrix), &myMaxMatrix);
  340.  
  341. bail:
  342.     return(myErr);
  343. }
  344.  
  345.  
  346. //////////
  347. //
  348. // QTWired_AddCursorChangeOnMouseOver
  349. // Add a cursor-change-on-mouse-over action to the sprite having the specified ID
  350. // in the specified atom container.
  351. //
  352. // Here we use the new "sprite behaviors atom" introduced in QuickTime 4.0, which
  353. // simplifies adding button-like capabilities to sprites.
  354. //
  355. //////////
  356.  
  357. OSErr QTWired_AddCursorChangeOnMouseOver (QTAtomContainer theContainer, QTAtomID theID)
  358. {
  359.     QTAtom                                mySpriteAtom = 0;
  360.     QTAtom                                myBehaviorAtom = 0;
  361.     QTSpriteButtonBehaviorStruct        myBehaviorRec;
  362.     OSErr                                myErr = noErr;
  363.     
  364.     // find the sprite atom with the specified ID in the specified container
  365.     mySpriteAtom = QTFindChildByID(theContainer, kParentAtomIsContainer, kSpriteAtomType, theID, NULL);
  366.     if (mySpriteAtom == 0) {
  367.         // if there is none, insert a new sprite atom into the specified container
  368.         myErr = QTInsertChild(theContainer, kParentAtomIsContainer, kSpriteAtomType, theID, 0, 0, NULL, &mySpriteAtom);
  369.         if (myErr != noErr)
  370.             goto bail;
  371.     }
  372.     
  373.     // insert a new sprite behaviors atom into the sprite atom
  374.     myErr = QTInsertChild(theContainer, mySpriteAtom, kSpriteBehaviorsAtomType, 1, 1, 0, NULL, &myBehaviorAtom);
  375.     if (myErr != noErr)
  376.         goto bail;
  377.  
  378.     //////////
  379.     //
  380.     // insert three atoms into the sprite behaviors atom; these three atoms specify what to do on each
  381.     // of the four defined state transitions for the (1) sprite image, (2) cursor, and (3) status string
  382.     //
  383.     //////////
  384.     
  385.     // set the sprite image behavior; -1 means: no change associated with this state transition
  386.     myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
  387.     myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(-1);
  388.     myBehaviorRec.overPressedStateID = EndianS32_NtoB(-1);
  389.     myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);
  390.  
  391.     myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteImageBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);
  392.     if (myErr != noErr)
  393.         goto bail;
  394.     
  395.     // set the sprite cursor behavior; -1 means: no change associated with this state transition
  396.     myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
  397.     myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(kQTCursorOpenHand);
  398.     myBehaviorRec.overPressedStateID = EndianS32_NtoB(-1);
  399.     myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);
  400.  
  401.     myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteCursorBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);
  402.     if (myErr != noErr)
  403.         goto bail;
  404.     
  405.     // set the status string behavior; -1 means: no change associated with this state transition
  406.     myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
  407.     myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(-1);
  408.     myBehaviorRec.overPressedStateID = EndianS32_NtoB(-1);
  409.     myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);
  410.  
  411.     myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteStatusStringsBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);
  412.     if (myErr != noErr)
  413.         goto bail;
  414.             
  415. bail:
  416.     return(myErr);
  417. }
  418.